<template>
  <div id="app">
    <h1 class="row">按↑↓←→操作</h1>
    <div class="game" :style="style">
      <div class="grade">当前分数：{{grade}}</div>
      <div
        v-show="item.value!==0"
        v-for="item in list"
        :key="item.id"
        :class="['item']"
        :style="position(item.x,item.y)"
      >
        <div
          :class="['item1',item.x===ani[0]&&item.y===ani[1]?'ani':'',color(item.value),anis(item.x,item.y)]"
        >{{item.value}}</div>
      </div>
    </div>
    <div class="row">
      <button @click="startGame(currentMode)">开始新的游戏</button>
      <button @click="startGame(4)">4x4</button>
      <button @click="startGame(5)">5x5</button>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
  components: {},
  data() {
    return {
      isMove: false,
      currentMode: 4,
      list: [],
      ani: [],
      aniList: [],
      grade: 0
    };
  },
  computed: {
    style() {
      return this.currentMode === 4
        ? {
            width: "161px",
            height: "161px"
          }
        : {
            width: "201px",
            height: "201px"
          };
    }
  },

  methods: {
    // 开始游戏
    startGame(e) {
      this.currentMode = e;
      this.list = [];
      this.random();
    },
    anis(x, y) {
      if (
        this.aniList.some(item => {
          return item[0] === x && item[1] === y;
        })
      ) {
        return "anis";
      }
    },
    // 随机数函数
    random() {
      // 所有的索引
      let arr1 = Array.from(
        new Array(this.currentMode * this.currentMode),
        (v, i) => i
      );
      let arr = this.list.filter(item => {
        return item.value !== 0;
      });
      // 剩下空白的数量
      const a = this.currentMode * this.currentMode - arr.length;
      if (a == 0) return;

      // 剩下空白中的第几个
      let b = Math.floor(Math.random() * a);
      // 已经占位的索引
      let arr2 = [];
      arr.forEach(item => {
        let ab = item.y * this.currentMode + item.x;
        arr2.push(ab);
      });
      // 剩下的索引
      let arr3 = [];
      arr1.forEach(item => {
        if (
          !arr2.some(a => {
            return a === item;
          })
        )
          arr3.push(item);
      });
      // b现在是总体中的索引
      b = arr3[b];
      this.list.push({
        value: Math.random() > 0.1 ? 2 : 4,
        x: b % this.currentMode,
        y: Math.floor(b / this.currentMode),
        isMerge: false,
        id: Math.random()
      });
      this.ani = [b % this.currentMode, Math.floor(b / this.currentMode)];
      setTimeout(() => {
        this.ani = [];
        this.aniList = [];
        this.isMove = false;
      }, 200);
      arr = this.list.filter(item => {
        return item.value !== 0;
      });

      if (arr.length === this.currentMode * this.currentMode) this.gameOver();
    },
    // 定位函数
    position(x, y) {
      return {
        transform: `translateX(${x * 40}px) translateY(${y * 40}px)`
      };
    },
    // 监听键盘函数
    keyboard(e) {
      if (
        e.keyCode !== 37 &&
        e.keyCode !== 38 &&
        e.keyCode !== 39 &&
        e.keyCode !== 40
      )
        return;
      if (this.isMove === true) return;
      this.isMove = true;
      let isRandom = false;
      switch (e.keyCode) {
        case 37:
          isRandom = this.left();
          break;
        case 38:
          isRandom = this.top();
          break;
        case 39:
          isRandom = this.right();
          break;
        case 40:
          isRandom = this.bottom();
          break;
      }
      if (!isRandom) return (this.isMove = false);
      // 给合成的加动画和变数字
      setTimeout(() => {
        const arr = [];

        this.list
          .filter(item => item.value !== 0)
          .forEach(item => {
            if (item.isMerge === true) {
              this.grade = this.grade + item.value;
              item.value = item.value * 2;
              arr.push([item.x, item.y]);
              item.isMerge = false;
            }
          });
        this.aniList = arr;
        //  等动画完了生成新的
        setTimeout(
          () => {
            this.random();
          },
          arr.length === 0 ? 10 : 310
        );
      }, 210);
    },
    // 左键
    left() {
      let result = false;
      for (let i = 0; i < this.currentMode; i++) {
        let arr = this.list.filter(item => {
          return item.y === i && item.value !== 0;
        });
        arr.sort((a, b) => {
          return a.x - b.x;
        });
        for (let index = 0; index < arr.length; index++) {
          if (index === 0 && arr[index].x !== 0) {
            arr[index].x = 0;
            result = true;
          } else if (
            index !== 0 &&
            arr[index].value === arr[index - 1].value &&
            arr[index - 1].isMerge === false
          ) {
            arr[index - 1].value = 0;
            arr[index].x = arr[index - 1].x;
            arr[index].isMerge = true;
            result = true;
          } else if (
            index !== 0 &&
            (arr[index - 1].isMerge === true ||
              arr[index].value !== arr[index - 1].value) &&
            arr[index - 1].x + 1 !== arr[index].x
          ) {
            arr[index].x = arr[index - 1].x + 1;
            result = true;
          }
        }
      }

      return result;
    },
    // 右键
    right() {
      let result = false;
      for (let i = 0; i < this.currentMode; i++) {
        let arr = this.list.filter(item => {
          return item.y === i && item.value !== 0;
        });
        if (arr.length === 0) continue;
        arr.sort((a, b) => {
          return a.x - b.x;
        });
        for (let index = arr.length - 1; index >= 0; index--) {
          if (
            index === arr.length - 1 &&
            arr[index].x !== this.currentMode - 1
          ) {
            arr[index].x = this.currentMode - 1;
            result = true;
          } else if (
            index !== arr.length - 1 &&
            arr[index].value === arr[index + 1].value &&
            arr[index + 1].isMerge === false
          ) {
            arr[index + 1].value = 0;
            arr[index].x = arr[index + 1].x;
            arr[index].isMerge = true;
            result = true;
          } else if (
            index !== arr.length - 1 &&
            (arr[index].value !== arr[index + 1].value ||
              arr[index + 1].isMerge !== false) &&
            arr[index].x !== arr[index + 1].x - 1
          ) {
            arr[index].x = arr[index + 1].x - 1;
            result = true;
          }
        }
      }
      return result;
    },
    // 上健
    top() {
      let result = false;
      for (let i = 0; i < this.currentMode; i++) {
        let arr = this.list.filter(item => {
          return item.x === i && item.value !== 0;
        });
        arr.sort((a, b) => {
          return a.y - b.y;
        });
        arr.forEach((item, i) => {
          if (i === 0 && item.y !== 0) {
            item.y = 0;
            result = true;
          } else if (
            i !== 0 &&
            arr[i].value === arr[i - 1].value &&
            arr[i - 1].isMerge === false
          ) {
            arr[i - 1].value = 0;
            arr[i].y = arr[i - 1].y;
            arr[i].isMerge = true;
            result = true;
          } else if (
            i !== 0 &&
            (arr[i - 1].isMerge === true || item.value !== arr[i - 1].value) &&
            arr[i - 1].y + 1 !== arr[i].y
          ) {
            arr[i].y = arr[i - 1].y + 1;
            result = true;
          }
        });
      }
      return result;
    },
    // 下键
    bottom() {
      let result = false;
      for (let i = 0; i < this.currentMode; i++) {
        let arr = this.list.filter(item => {
          return item.x === i && item.value !== 0;
        });
        if (arr.length === 0) continue;
        arr.sort((a, b) => {
          return a.y - b.y;
        });
        for (let index = arr.length - 1; index >= 0; index--) {
          if (
            index === arr.length - 1 &&
            arr[index].y !== this.currentMode - 1
          ) {
            arr[index].y = this.currentMode - 1;
            result = true;
          } else if (
            index !== arr.length - 1 &&
            arr[index].value === arr[index + 1].value &&
            arr[index + 1].isMerge === false
          ) {
            arr[index + 1].value = 0;
            arr[index].y = arr[index + 1].y;
            arr[index].isMerge = true;
            result = true;
          } else if (
            index !== arr.length - 1 &&
            (arr[index].value !== arr[index + 1].value ||
              arr[index + 1].isMerge !== false) &&
            arr[index].y !== arr[index + 1].y - 1
          ) {
            arr[index].y = arr[index + 1].y - 1;
            result = true;
          }
        }
      }
      return result;
    },
    gameOver() {
      const arr = this.list.filter(item => {
        return item.value !== 0;
      });
      const result = arr.some(item => {
        const x = item.x;
        const y = item.y;
        const value = item.value;
        const arr1 = arr.filter(item => {
          return (
            (item.y === y && item.x === x + 1) ||
            (item.y === y && item.x === x - 1) ||
            (item.y === y - 1 && item.x === x) ||
            (item.y === y - 1 && item.x === x)
          );
        });
        return arr1.some(item => {
          return item.value === value;
        });
      });
      if (!result)
        setTimeout(() => {
          alert("游戏结束");
        }, 100);
    },
    copy(a) {
      let b;
      switch (typeof a) {
        case "object":
          if (a instanceof Array) {
            let arr1 = [];
            a.forEach(item => {
              arr1.push(this.copy(item));
            });
            b = arr1;
          } else {
            let obj = {};
            Object.keys(a).forEach(item => {
              obj[item] = this.copy(a[item]);
            });
            b = obj;
          }
          break;
        default:
          b = a;
          break;
      }
      return b;
    },
    // 颜色
    color(v) {
      return "box" + v;
    }
  },
  created() {
    document.addEventListener("keyup", this.keyboard);
  }
};
</script>

<style>
.grade {
  position: relative;
  left: 100%;
  transform: translate(10px);
}
.row {
  display: flex;
  justify-content: center;
}
.item {
  height: 40px;
  width: 40px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.item1 {
  height: 35px;
  width: 35px;
  line-height: 35px;
  text-align: center;
  border-radius: 5px;
}
button {
  margin: 20px 20px;
  height: 30px;
  border-radius: 15px;
  background: rgb(112, 205, 218);
  color: #ffffff;
  border: none;
  cursor: pointer;
}
@keyframes move {
  0% {
    transform: scale(0);
  }
  75% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(1);
  }
}
.anis {
  animation: move 0.2s forwards;
}
.ani {
  animation: move 0.2s forwards;
}
.game {
  background: #bbada0;
  border-radius: 5px;
  margin: 20px auto 0;
  position: relative;
}
* {
  box-sizing: border-box;
}
.item {
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  border: 3px solid #bbada0;
  width: 40px;
  height: 40px;
  border-radius: 8px;
  transition: all 0.2s;
}

.box2 {
  background: #eee4da;
  color: #7a6d65;
}
.box4 {
  background: #ebdcbd;
  color: #7a6d65;
}
.box8 {
  background: #f2b179;
  color: #ffffff;
}
.box16 {
  background: #f7925c;
  color: #ffffff;
}
.box32 {
  background: #f57656;
  color: #ffffff;
}
.box64 {
  background: #f4522c;
  color: #ffffff;
}
.box128 {
  background: #edce71;
  color: #ffffff;
}
.box256 {
  background: #ff3030;
  color: #ffffff;
}
.box512 {
  background: #4385ff;
  color: #ffffff;
}
.box1024 {
  background: #67e99d;
  color: #ffffff;
  font-size: 12px;
}
.box2048 {
  background: #ff3030;
  color: #ffffff;
  font-size: 12px;
}
</style>
